package com.thoughtorigin.flux {
import com.thoughtorigin.flux.asflux.ActionscriptFlux;
import com.thoughtorigin.flux.asflux.ActionscriptFluxSettings;
import com.thoughtorigin.flux.asflux.Execution;
import com.thoughtorigin.flux.asflux.ObjectDef;
import com.thoughtorigin.flux.asflux.ReferenceArgument;
import com.thoughtorigin.flux.asflux.ReferenceProperty;
import com.thoughtorigin.flux.asflux.ValueArgument;
import com.thoughtorigin.flux.asflux.ValueProperty;
import com.thoughtorigin.flux.fluxmocks.Sample;
import com.thoughtorigin.flux.fluxmocks.SampleProperties;

import com.thoughtorigin.flux.fluxmocks.SampleWithConstructor;
import com.thoughtorigin.flux.fluxmocks.SampleWithRefConstructor;

import org.flexunit.asserts.assertEquals;
import org.flexunit.asserts.assertFalse;
import org.flexunit.asserts.assertNotNull;
import org.flexunit.asserts.assertTrue;
import org.flexunit.asserts.fail;

public class ActionscriptFluxTest {

    private var _asIOC: ActionscriptFlux;

    [Before]
    public function setup(): void {
        var settings: ActionscriptFluxSettings = new ActionscriptFluxSettings();
        _asIOC = new ActionscriptFlux();

        var sampleDef: ObjectDef = new ObjectDef(Sample);
        settings.addObjectDef("sample", sampleDef);

        sampleDef = new ObjectDef(null);
        settings.addObjectDef("sample2", sampleDef);

        sampleDef = new ObjectDef(SampleProperties);
        sampleDef.addProperty(new ValueProperty("blam", "kamblamo"));
        settings.addObjectDef("umbrella", sampleDef);

        sampleDef = new ObjectDef(SampleWithConstructor);
        sampleDef.addConstructorArg(new ValueArgument("sampleArg"))
                 .addConstructorArg(new ValueArgument("sampleArg2"));
        settings.addObjectDef("sample3", sampleDef);

        sampleDef = new ObjectDef(SampleWithConstructor);
        sampleDef.addConstructorArg(new ValueArgument("sampleArg"));
        settings.addObjectDef("sample4", sampleDef);

        sampleDef = new ObjectDef(SampleProperties);
        sampleDef.addExecution(new Execution("sampleFunction"));
        settings.addObjectDef("sample5", sampleDef);

        sampleDef = new ObjectDef(SampleProperties);
        sampleDef.addExecution(new Execution("sampleArgFunction")
                .addArgument(new ValueArgument("value1"))
                .addArgument(new ValueArgument("value2")));
        settings.addObjectDef("sample6", sampleDef);

        sampleDef = new ObjectDef(SampleProperties)
            .addProperty(new ReferenceProperty("sample", _asIOC, "sample"));
        settings.addObjectDef("sample7", sampleDef);

        sampleDef = new ObjectDef(SampleWithRefConstructor)
            .addConstructorArg(new ReferenceArgument("sample7", _asIOC));
        settings.addObjectDef("sample8", sampleDef);

        sampleDef = new ObjectDef(SampleProperties)
            .addExecution(new Execution("sampleArgsFunctionWithRef").addArgument(new ReferenceArgument("sample3", _asIOC)));
        settings.addObjectDef("sample9", sampleDef);

        settings.addObjectDef("sample10", new ObjectDef(Sample, true))
                .addObjectDef("sample11", new ObjectDef(SampleProperties).addProperty(new ReferenceProperty("sample", _asIOC, "sample", true)));

        _asIOC.settings = settings;
    }

    [Test]
    public function shouldReturnAValidProperty(): void {
        var property: Object = _asIOC.getProperty("sample");

        assertNotNull(property);
        assertTrue(property is com.thoughtorigin.flux.fluxmocks.Sample);
    }

    [Test]
    public function shouldThrowAnExceptionIfPropertyIsNotFound(): void {
        try {
            var property: Object = _asIOC.getProperty("cannot find this");
            fail();
        } catch(e: PropertyNotFoundException) {
            assertTrue(true);
        }
    }

    [Test]
    public function shouldOnlyCreateOneInstanceOfTheObject(): void {
        var propertyA: Object = _asIOC.getProperty("sample");
        var propertyB: Object = _asIOC.getProperty("sample");

        assertEquals(propertyA, propertyB);
    }

    [Test]
    public function shouldCreateAProperty(): void {
        var obj: Object = _asIOC.createNew("sample");
        assertNotNull(obj);
        assertTrue(obj is Sample);
    }

    [Test]
    public function shouldCreateANewInstanceEveryTime(): void {
        var propertyA: Object = _asIOC.createNew("sample");
        var propertyB: Object = _asIOC.createNew("sample");

        assertTrue(propertyA != propertyB);
    }

    [Test]
    public function shouldReturnAClassType(): void {
        var clazz: Class = _asIOC.getClass("sample");
        assertNotNull(clazz);
        assertTrue(clazz is Class);
        assertTrue(new clazz() is Sample);
    }

    [Test]
    public function shouldThrowAnExceptionIfTheClassIsNotFound(): void {
        try {
            var property: Class = _asIOC.getClass("sample2");
            fail();
        } catch(e: ClassDefinitionNotFoundException) {
            assertTrue(true);
        }
    }

    [Test]
    public function shouldCreateAInstanceAndAssignProperties(): void {
        var obj: SampleProperties = SampleProperties(_asIOC.getProperty("umbrella"));
        assertNotNull(obj);
        assertEquals("kamblamo", obj.blam);
    }

    [Test]
    public function shouldAssignConstructorArgs(): void {
        var obj: SampleWithConstructor = SampleWithConstructor(_asIOC.getProperty("sample3"));
        assertEquals("sampleArg", obj.arg1);
        assertEquals("sampleArg2", obj.arg2);
    }

    [Test]
    public function shouldThrowExceptionIfArgMismatch(): void {
        try {
            var obj: SampleWithConstructor = SampleWithConstructor(_asIOC.getProperty("sample4"));
            fail();
        } catch (e: ArgumentError) {
            assertTrue(true);
        }
    }

    [Test]
    public function shouldExecuteFunctions(): void {
        var obj: SampleProperties = SampleProperties(_asIOC.getProperty("sample5"));
        assertTrue(obj.sampleFunctionValue);
    }

    [Test]
    public function shouldExecuteFunctionsWithArgs(): void {
        var obj: SampleProperties = SampleProperties(_asIOC.getProperty("sample6"));
        assertEquals("value1", obj.arg1);
        assertEquals("value2", obj.arg2);
    }

    [Test]
    public function shouldCreateInstanceAndPassReferenceVariablesAndAssignToProperties(): void {
        var obj: SampleProperties = SampleProperties(_asIOC.getProperty("sample7"));
        assertNotNull(obj.sample);
        assertTrue(obj.sample is Sample);
    }

    [Test]
    public function shouldCreateInstanceAndPassReferenceVariablesAndAssignToConstructor(): void {
        var obj: SampleWithRefConstructor = SampleWithRefConstructor(_asIOC.getProperty("sample8"));
        assertNotNull(obj.sample);
        assertNotNull(obj.sample.sample);
    }

    [Test]
    public function shouldCreateInstanceAndPassReferenceVariablesAndAssignToFunction(): void {
        var obj: SampleProperties = SampleProperties(_asIOC.getProperty("sample9"));
        assertNotNull(obj.sampleArgsRef);
        assertEquals("sampleArg", obj.sampleArgsRef.arg1);
    }

    [Test]
    public function shouldCreateOneSingleInstance(): void {
        var obj: Sample = Sample(_asIOC.getProperty("sample"));
        var obj2: SampleProperties = SampleProperties(_asIOC.getProperty("sample7"));

        assertEquals(obj, obj2.sample);
    }

    [Test]
    public function shouldCreateNewInstanceIfExplicitlyConfiguredToCreateNewInstances(): void {
        var obj: Sample = Sample(_asIOC.getProperty("sample10"));
        var obj2: Sample = Sample(_asIOC.getProperty("sample10"));

        assertFalse(obj == obj2);

        var obj3: Sample = Sample(_asIOC.getProperty("sample10"));
        var obj4: SampleProperties = SampleProperties(_asIOC.getProperty("sample11"));

        assertFalse(obj3 == obj4.sample);
    }

    [Test]
    public function shouldCreateNewInstanceAndNotOverwriteCache(): void {
        var obj: Sample = Sample(_asIOC.getProperty("sample"));
        var obj2: Sample = Sample(_asIOC.createNew("sample"));
        var obj3: Sample = Sample(_asIOC.getProperty("sample"));

        assertFalse(obj == obj2);
        assertEquals(obj, obj3);
    }

    [Test]
    public function shouldCreateNewInstanceWithAddtionalPropertiesAndExecutions(): void {
        var obj: SampleWithRefConstructor = SampleWithRefConstructor(_asIOC.createNew("sample8"));
        assertNotNull(obj);
        assertNotNull(obj.sample);

        var obj2: SampleProperties = SampleProperties(_asIOC.createNew("sample6"));
        assertNotNull(obj2);
        assertEquals("value1", obj2.arg1);
        assertEquals("value2", obj2.arg2);

        obj2 = SampleProperties(_asIOC.createNew("sample7"));
        assertNotNull(obj2.sample);

        obj2 = SampleProperties(_asIOC.createNew("sample9"));
        assertEquals("sampleArg", obj2.sampleArgsRef.arg1);
    }
}
}